﻿namespace Blaze.Framework.Loaders
{
    using System.Collections;
    using UnityEngine;
    using UnityEngine.SceneManagement;

    /// <summary>
    /// 用于加载场景。
    /// </summary>
    public class SceneLoader : AssetLoaderBase
    {
        /// <summary>
        /// 获取资源包名。
        /// </summary>
        public string AssetBundleName { get; private set; }

        /// <summary>
        /// 获取或设置加载场景的模式。
        /// </summary>
        public LoadSceneMode LoadSceneMode { get; set; }

        /// <summary>
        /// 获取加载进度。
        /// </summary>
        public override float Progress
        {
            get
            {
                if (mAssetBundleLoader != null)
                    return (mAssetBundleLoader.Progress + mLoadSceneProgress)/2;
                return mLoadSceneProgress;
            }
        }

        /// <summary>
        /// 获取场景路径名。
        /// </summary>
        public string SceneName { get; private set; }

        /// <summary>
        /// 设置场景加载模式为LoadSceneMode.Additive"。
        /// </summary>
        public SceneLoader AsAdditive()
        {
            LoadSceneMode = LoadSceneMode.Additive;
            return this;
        }

        /// <summary>
        /// 设置场景加载模式为LoadSceneMode.Single。
        /// </summary>
        public SceneLoader AsSingle()
        {
            LoadSceneMode = LoadSceneMode.Single;
            return this;
        }

        /// <summary>
        /// 当将该对象存到池中调用此方法。
        /// </summary>
        public override void OnPut()
        {
            base.OnPut();
            SceneManager.UnloadSceneAsync(mLoadedScene);
            if (mAssetBundleLoader != null)
            {
                AssetLoaderPool.Put(mAssetBundleLoader);
                mAssetBundleLoader = null;
            }
        }

        /// <summary>
        /// 当加载器启动时调用此方法。
        /// </summary>
        protected override void OnStart()
        {
            var array = Uri.Split('&');
            AssetBundleName = array[0];
            SceneName = array[1];


#if UNITY_EDITOR
            if (AppConfigManager.Get<AppConfig>().simulate_runtime)
                CoroutineManager.StartNew(loadFromAssetBundle());
            else
                CoroutineManager.StartNew(loadScene());
#else
            CoroutineManager.StartNew(loadFromAssetBundle());
#endif
        }

        private IEnumerator loadFromAssetBundle()
        {
            mAssetBundleLoader = AssetLoaderPool.Get<AssetBundleLoader>(AssetBundleName);
            mAssetBundleLoader.Mode = Mode;
            mAssetBundleLoader.Start();
            while (!mAssetBundleLoader.IsDone)
                yield return null;
            yield return CoroutineManager.StartNew(loadScene());
        }

        private IEnumerator loadScene()
        {
            if (Mode == LoadAssetMode.Sync)
            {
                SceneManager.LoadScene(SceneName, LoadSceneMode);
            }
            else
            {
                var operation = SceneManager.LoadSceneAsync(SceneName, LoadSceneMode);
                while (!operation.isDone)
                {
                    mLoadSceneProgress = operation.progress;
                    yield return null;
                }
            }
            mLoadSceneProgress = 1;
            mLoadedScene = SceneManager.GetSceneByName(SceneName);
            Finish(null, mLoadedScene);
#if UNITY_EDITOR
            if (AppConfigManager.Get<AppConfig>().fix_shader_reference)
            {
                yield return null;
                fixShaderReferences(mLoadedScene);
            }
#endif
        }

        private AssetBundleLoader mAssetBundleLoader;
        private float mLoadSceneProgress;
        private Scene mLoadedScene;

#if UNITY_EDITOR
        private static void fixShaderReferences(Scene scene)
        {
            foreach (var obj in scene.GetRootGameObjects())
                fixShaderReferences(obj);
        }

        private static void fixShaderReferences(GameObject gameObject)
        {
            var renderers = gameObject.GetComponentsInChildren<Renderer>();
            foreach (var renderer in renderers)
            {
                if (renderer.sharedMaterials == null)
                    continue;
                foreach (var material in renderer.sharedMaterials)
                {
                    if (material == null || material.shader == null)
                        continue;

                    material.shader = Shader.Find(material.shader.name);
                }
            }
        }
#endif
    }
}